#pragma once
#include <iostream>
#include <string>
#include <list>
#include <windows.h>

using namespace std;

namespace raytracer{

	//Prototypes of all classes belonging to raytracer namespace	
	class Vector;
	class Ray;	
	class Intersect;		
	class Object;
	class Sphere;
	class Plane;	
	class Camera;
	class Light;
	class Scene;
	class Raytracer;
	class Bitmap;
	
	class Vector{
	public:
		double X;
		double Y;
		double Z;
		Vector();
		Vector(double x, double y, double z);
		Vector(string &s);
		static Vector times(double n, Vector &v);
		static Vector minus(Vector &v1, Vector &v2);
		static Vector plus(Vector &v1, Vector &v2);
		static double dot(Vector &v1, Vector &v2);
		static double magnitude(Vector &v);
		static Vector normalize(Vector &v); 
		static Vector cross(Vector &v1, Vector &v2);
		static bool equals(Vector &v1, Vector &v2);		
		static double angleBetween(Vector &v1, Vector &v2);
	};

	class Ray{
	public:
		Ray();
		Vector start;
		Vector direction;
	};

	class Intersect{
	public:
		Intersect();
		Ray Ray;
		double distance;
		Object* object;
	};	
	
	class Color{		
	public:
		double R;
		double G;
		double B;
		Color();
		Color(double r, double g, double b);
		Color(string &s);
		static Color times(double n, Color &v);
		static Color times(Color &c1, Color &c2);
		static Color plus(Color &c1, Color &c2);
        static Color minus(Color &c1, Color &c2);
		static bool equals(Color &c1, Color &c2);		
	};

	class Surface{
	public:
		Surface();
		virtual Color diffuse(Vector &v);
		virtual Color specular(Vector &v);
		virtual double reflect(Vector &v);
		double roughness;
	};

	class Object{
	public:		
		Object();
		Object(const Object& object);
        virtual bool intersect(Ray &ray, Intersect &intersect);
        virtual Vector normal(Vector &pos);	
		Surface* surface;
	};

	class Sphere : public Object {
	public:		
		Sphere();
		Sphere(const Sphere& sphere);
		Vector position;
		double radius;
		bool intersect(Ray &ray, Intersect &intersect);
		Vector normal(Vector &position);
	};

	class Plane : public Object{
	public:		
		Plane();
		Plane(const Plane& plane);
		Vector myNormal;
		Vector normal(Vector &pos);
		double offset;
		bool intersect(Ray &ray, Intersect &intersect);
	};

	class CheckerBoard_Surface : public Surface{
	public: 
		CheckerBoard_Surface();
		Color diffuse(Vector &v);
		Color specular(Vector &v);
		double reflect(Vector &v);
    };

	class Shiny_Surface : public Surface{
	public: 
		Shiny_Surface();
		Color diffuse(Vector &v);
		Color specular(Vector &v);
		double reflect(Vector &v);
    };

	class Camera
    {
	public: 
		Vector position;
        Vector forward;
        Vector up;
        Vector right;
		Camera();
        Camera(Vector position, Vector lookAt);
    };

	class Light{
	public: 
		Vector position;
        Color color;
    };

	class Scene{
	public:
		Scene();
		list<Object*> things;
        list<Light*> lights;
        Camera camera;
        list<Intersect> intersect(Ray r);
	};	

	class Bitmap
	{
	private:
		unsigned char* buffer;
		int Width;
		int Height;
	public:
		Bitmap(int width, int height);
		Bitmap();
		Bitmap(const Bitmap& bitmap);
		~Bitmap(void);
		void save(string filename);
		void load(string filename);
		void drawPoint(int x, int y, Color &c);
		Color getColorOfPoint(int x, int y);
		void drawLine(int x0, int y0, int x1, int y1, Color &c);
		void drawRectangle(int x0, int y0, int x1, int y1, Color &c);
		void drawCircle(int x0, int y0, int radius, Color &c);
		void superSampleBitmap(Bitmap& bitmap);
		int getWidth();
		int getHeight();
	};

	class Raytracer{
	private:
		Scene scene;
		Bitmap bitmap;
		int Maxdepth;
		list<Intersect> Intersections(Ray &ray, Scene &scene);
		int totalwidth;
		int totalheight;
		int left;
		int top;
		double testRay(Ray &ray, Scene &scene);		
		Color getNaturalColor(Object &thing, Vector &pos, Vector &norm, Vector &rd, Scene &scene);
		Color getReflectionColor(Object &thing, Vector &pos, Vector &norm, Vector &rd, Scene &scene, int depth);
		Color shade(Intersect &isect, Scene &scene, int depth);
		double recenterX(double x);
		double recenterY(double y);			
	public:
		Raytracer(Bitmap &bitmap, int maxdepth,int totalwidth, int totalheight, int left, int top);		
		void render(Scene &scene);
		Vector getPoint(double x, double y, Camera &camera);
		Color traceRay(Ray &ray, Scene &scene, int depth);
	};

	Vector operator+ (Vector &v1,Vector &v2);
	Vector operator- (Vector &v1,Vector &v2);
	Vector operator* (double n,Vector &v2);
	double operator* (Vector &v1,Vector &v2);
	Vector operator^ (Vector &v1,Vector &v2);
	bool operator== (Vector &v1,Vector &v2);

	ostream & operator<< (ostream &os, const Vector &v);
	ostream & operator<< (ostream &os, const Color &c);
	ostream & operator<< (ostream &os, const Ray &ray);

	Color operator+ (Color &c1,Color &c2);
	Color operator- (Color &c1,Color &c2);
	Color operator* (double n,Color &c);
	Color operator* (Color &c1,Color &c2);
	bool operator== (Color &c1,Color &c2);

	struct ThreadData
	{
		Bitmap* bitmap;
		Scene* scene;
		Raytracer* raytraycer;
		bool even;
		int left;
		int top;
		int totalwidth;
		int totalheight;
	};
}